package mymyoux.io
{
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;
	
	import mymyoux.events.ErrorEvent;
	import mymyoux.events.MEventDispatcher;
	/**
	 * MSocket permet de créer un Socket dont la gestion est simplifiée et améliorée.
	 * @author Mymyoux
	 * @version 1.0
	 */
	public class MSocket extends MEventDispatcher
	{
		//** public constants **//
		static public const ONLINE:String="online",OFFLINE:String="offline";
		//** private variables **//
		private var _s:Socket;
		private var _t:Tampon;
		protected var _ip:String;
		protected var _port:uint;
		private var _interval:uint;
		private var _connected:Boolean;
		//** public functions **//
		public function MSocket()
		{
			_t=new Tampon();
			_connected=false;
		}
		public function start(ip:String="",port:uint=0):void
		{
			if(ip.length>0 && port>=1024 && port <=65535){
				if(_connected){//deleting of previous connexion
					_connected=false;
					_stop();
				}
				_ip=ip;
				_port=port;
				_start();
			}else{
				throw(new ErrorEvent("port ou ip incorrect",3));
			}
		}
		public function get state():String{
			return _connected?ONLINE:OFFLINE;
		}
		public function get connected():Boolean{
			return _connected;
		}
		public function restart():void{
			_restart();
		}
		public override function destroy():void{
			this.stop();
			onClose();//TODO:essai
			super.destroy();
		}
		//** protected functions **//
		protected function _send(str:String):void{
			trace(str);
			if(_s!=null && _s.connected){
				__send(str);
			}else{
				_t.add(str);
			}
		}
		protected function _videFlush():void{
			if(_s.connected){
				while(_t.hasNext()){
					__send(_t.next());
				}
			}
		}
		protected function stop():void{
			_stop();
		}
		protected function onConnect():void{//retourne vrai si on doit vider le tampon
			//to overriding
			//return true;
		}
		protected function onError():void{
			//to overriding
		}
		protected function onClose():void{
			//to overriding
		}
		protected function onData(data:String):void{
			trace(data);
			//to overriding
		}
		//** private functions **//
		private function __send(str:String):void{
			if(str.length>0){
			//	MLog.log("out:"+str);
				if(_s.connected){
					_s.writeUTFBytes(str);
					_s.flush();
				}else{
					_t.add(str);
				}
			}
		}
		private function _start():void{
				_interval=setInterval(_noop,10000);//force flash to see disconnecting
				try{
				_s=new Socket();
				_s.addEventListener(Event.CONNECT,_onConnect);
				_s.addEventListener(IOErrorEvent.IO_ERROR,_onIOError);
				_s.addEventListener(SecurityErrorEvent.SECURITY_ERROR,_onSecError);
				_s.addEventListener(ProgressEvent.SOCKET_DATA,_onData);
				_s.addEventListener(Event.CLOSE,_onClose);
				_s.connect(_ip,_port);
				_connected=true;
				}catch(e:IOErrorEvent){
					trace("hum : "+e);
				}
		}
		private function _stop():void{
			clearInterval(_interval);
			_s.removeEventListener(Event.CONNECT,_onConnect);
			if(_s.connected){
				_s.close();
			}
		}
		private function _restart():void{
			_stop();
			_start();
		}
		private function _onConnect(e:Event):void{
			_connected=true;
			//if(onConnect()){
				_videFlush();
			//}
			onConnect();
		}
		private function _onIOError(e:IOErrorEvent):void{
			if(e.target===_s){
				_connected=false;
				onError();
			}
			e.target.removeEventListener(Event.CONNECT,_onConnect);
			e.target.removeEventListener(IOErrorEvent.IO_ERROR,_onIOError);
			e.target.removeEventListener(ProgressEvent.SOCKET_DATA,_onData);
			e.target.removeEventListener(Event.CLOSE,_onClose);
		}
		private function _onSecError(e:SecurityErrorEvent):void{
			if(e.target===_s){
				_connected=false;
				onError();
			}
			e.target.removeEventListener(Event.CONNECT,_onConnect);
			e.target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,_onSecError);
			e.target.removeEventListener(ProgressEvent.SOCKET_DATA,_onData);
			e.target.removeEventListener(Event.CLOSE,_onClose);
		}
		private function _onData(e:ProgressEvent):void{
			if(e.target===_s){
				while(_s.bytesAvailable>0){
					onData(_s.readUTFBytes(_s.bytesAvailable));
				}
			}
		}
		private function _onClose(e:Event):void{
			if(e.target===_s){
				_connected=false;
				onClose();
			}
			e.target.removeEventListener(Event.CONNECT,_onConnect);
			e.target.removeEventListener(ProgressEvent.SOCKET_DATA,_onData);
			e.target.removeEventListener(Event.CLOSE,_onClose);
		}
		private function _noop():void{
			_s.connected;
		}
	}
}
internal class Tampon{
	private var _tamp:Array;
	public function Tampon(){
		_tamp=new Array();
	}
	public function add(s:String):void{
		_tamp.push(s);
	}
	public function hasNext():Boolean{
		return _tamp.length>0;
	}
	public function next():String{
		if(hasNext()){
			return _tamp.shift();
		}else{
			return null;
		}
	}
	public function get length():uint{
		return _tamp.length;
	}
}